
const puppeteer = require('puppeteer-core');

const fs = require("fs");
const dayjs = require("dayjs");
const path = require("path");

/**
 * 等待包含指定文案的元素出现
 * @param {Object} page - Puppeteer 的 page 实例
 * @param {string} tagName - 元素的标签名，例如 'button', 'span' 等
 * @param {string} text - 要匹配的文案
 * @param {boolean} [exactMatch=false] - 是否精确匹配文案，默认为模糊匹配
 * @param {number} [targetIndex=0] - 目标元素的索引，默认为 0
 * @param {number} [timeout=10000] - 最大等待时间，单位为毫秒，默认为 10 秒
 * @param {number} [interval=1000] - 检查间隔时间，单位为毫秒，默认为 1 秒
 * @returns {Promise<boolean>} - 如果元素出现返回 true，超时则返回 false
 */
async function waitForElementWithText(page, tagName, text, exactMatch = false, targetIndex = 0, timeout = 10000, interval = 1000) {
    const startTime = Date.now();
    while (Date.now() - startTime < timeout) {
        if (await hasElementWithText(page, tagName, text, exactMatch, targetIndex)) {
            return true;
        }
        await delay(interval);
    }
    console.log(`等待包含 ${exactMatch ? '精确' : '模糊'} 文案 "${text}" 的 ${tagName} 元素超时`);
    return false;
}
// 定义一个函数用于等待指定毫秒数
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
/**
 * 等待元素出现后点击包含指定文案的元素
 * @param {Object} page - Puppeteer 的 page 实例
 * @param {string} tagName - 元素的标签名，例如 'button', 'span' 等
 * @param {string} text - 要匹配的文案
 * @param {boolean} [exactMatch=false] - 是否精确匹配文案，默认为模糊匹配
 * @param {number} [targetIndex=0] - 目标元素的索引，默认为 0
 * @param {number} [timeout=10000] - 最大等待时间，单位为毫秒，默认为 10 秒
 * @param {number} [interval=1000] - 检查间隔时间，单位为毫秒，默认为 1 秒
 * @returns {Promise<boolean>} - 如果成功点击返回 true，否则返回 false
 */
async function clickElementWithText(page, tagName, text, exactMatch = false, targetIndex = 0, timeout = 10000, interval = 1000) {
    // 先等待元素出现
    const isElementAppeared = await waitForElementWithText(page, tagName, text, exactMatch, targetIndex, timeout, interval);
    if (isElementAppeared) {
        try {
            const allElements = await page.$$(tagName);
            let index = targetIndex;
            for (const element of allElements) {
                const elementText = await page.evaluate(el => el.textContent.trim(), element);
                if (exactMatch ? elementText === text : elementText.includes(text)) {
                    if (index > 0) {
                        index--;
                        continue;
                    }
                    await element.click();
                    console.log(`成功点击包含第${targetIndex}个 ${exactMatch ? '精确' : '模糊'} 文案 "${text}" 的 ${tagName} 元素`);
                    return true;
                }
            }
            console.log(`未找到包含 ${exactMatch ? '精确' : '模糊'} 文案 "${text}" 的 ${tagName} 元素`);
            return false;
        } catch (error) {
            console.error(`点击包含文案 "${text}" 的 ${tagName} 元素时出错:`, error);
            return false;
        }
    } else {
        console.log(`等待包含 ${exactMatch ? '精确' : '模糊'} 文案 "${text}" 的 ${tagName} 元素超时，未进行点击操作`);
        return false;
    }
}
/**
 * 判断是否存在包含指定文案的元素
 * @param {Object} page - Puppeteer 的 page 实例
 * @param {string} tagName - 元素的标签名，例如 'button', 'span' 等
 * @param {string} text - 要匹配的文案
 * @param {boolean} [exactMatch=false] - 是否精确匹配文案，默认为模糊匹配
 * @param {number} [targetIndex=0] - 目标元素的索引，默认为 0
 * @returns {Promise<boolean>} - 如果存在返回 true，否则返回 false
 */
async function hasElementWithText(page, tagName, text, exactMatch = false, targetIndex = 0) {
    try {
        const allElements = await page.$$(tagName);
        let index = targetIndex;
        for (const element of allElements) {
            const elementText = await page.evaluate(el => el.textContent.trim(), element);
            if (exactMatch ? elementText === text : elementText.includes(text)) {
                if (index > 0) {
                    index--;
                    continue;
                }
                return true;
            }
        }
        return false;
    } catch (error) {
        console.error(`判断是否存在包含文案 "${text}" 的 ${tagName} 元素时出错:`, error);
        return false;
    }
}

const pkgList =require('./pkgList-buyp')


let page
let browser
let hasStartPuppeteer=false

const view={
    async init(){
        // 启动 Puppeteer
        await this.startPuppeteer()
        try {
            await this.buyAll()
        }catch (e){
            console.error(e)
        }
        // await this.toString()
        console.log('买票任务结束')
    },
    async buyAll(){
        // await this.updateLocal()
        await this.startPuppeteer()
        //打开华为开发者平台
        for(let i=0;i<pkgList.length;i++){
            const pkgItem=pkgList[i];
            if(!pkgItem.hasBuy){
                await this.buildOne(pkgItem)
            }

        }
        // await this.closePuppeteer()

    },
    //获取上传包的状态，然后上传
    async buildOne(pkgItem){
        //进入列表页
        const currentUrl = await page.url();
        const listUrl=pkgItem.url
        if(currentUrl===listUrl){
            console.log('已进入查询页')
        }else{
            //进入列表页
            await page.goto(listUrl);
        }
        await page.waitForSelector('#queryLeftTable');
        let tableData = await page.evaluate(() => {
            const rows = document.querySelectorAll("#queryLeftTable tr")
            const products = [];
            let indexBtn=0
            // 遍历每一行
            for (let i = 0; i < rows.length; i++) {
                const row = rows[i];
                // 检查行是否为隐藏状态
                const style = window.getComputedStyle(row);
                if (style.display === 'none') {
                    continue;
                }
                const nodeMap={
                    checi:row.querySelector('a.number'),//车次
                    startT:row.querySelector('.cds strong.start-t'),//出发时间
                    endT:row.querySelector('.cds strong.color999'),//到达时间
                    dT:row.querySelector('div.ls'),//历时
                    // 选择 aria-label 包含 "商务座" 的 td 元素
                    swzNum: row.querySelector('td[aria-label*="商务座"]'),
                    ydzNum: row.querySelector('td[aria-label*="一等座"]'),
                    edzNum: row.querySelector('td[aria-label*="二等座"]'),
                    ydBtn: row.querySelector('td.no-br a'),

                }
                for(let k in nodeMap){
                    nodeMap[k]=nodeMap[k]?nodeMap[k].textContent.trim():''
                }
                nodeMap.childN=i
                if(nodeMap.ydBtn=='预订'){
                    nodeMap.index=indexBtn
                    indexBtn++
                }

                nodeMap.sortNum=-1
                products.push(nodeMap);
            }

            return products;
        });
        console.log('table 中的所有产品:', tableData);

        //过滤
        if(pkgItem.buyFilter){
            pkgItem.buyFilter.forEach(filItem=>{
                if(filItem.type==='checi'){
                    tableData=tableData.filter((item)=>{
                        return item.checi===filItem.checi
                    })
                }
                if(filItem.type==='time'){
                    tableData=tableData.filter((item)=>{
                        const arr=filItem.data
                        return arr[0]<=item.startT&&arr[1]>=item.startT
                    })
                }
                if(filItem.type==='二等座'){
                    tableData=tableData.filter((item)=>{
                        return item.edzNum>0||item.edzNum==='有'||item.edzNum==='候补'
                    })
                }
            })

        }
        console.log('table 中的过滤产品:', tableData);
        if(pkgItem.buySort){
            let sortNum=100
            for(let i=0;i<pkgItem.buySort.length;i++){
                const sort=pkgItem.buySort[i]
                if(sort.type==='checi'){
                    tableData.forEach((item)=>{
                        if(sort.checi===item.checi){
                            item.sortNum=sortNum--
                        }
                    })
                }
            }
            tableData.sort((a,b)=>{
                return b.sortNum-a.sortNum
            })
        }
        console.log('table 中的排序产品:', tableData);
        if(tableData.length>0){
            pkgItem.hasBuy=true
            const buyInfo=tableData[0]
            // 点击对应的行
            const rowIndex = buyInfo.childN;
            const rowSelector = `#queryLeftTable tr:nth-child(${rowIndex + 1}) td[aria-label*="二等座"]`;
            try {
                await page.waitForSelector(rowSelector, { timeout: 5000 });
                await page.click(rowSelector);
                console.log(`成功点击第 ${rowIndex + 1} 行`);
            } catch (error) {
                console.error(`点击第 ${rowIndex + 1} 行时出错:`, error);
            }
            await delay(2000)
            await page.click('#is_open .switch');
            await page.click(`label[title=${pkgItem.buyName}]`);
            await delay(1000)
            await page.click('#toPayBtn');
            await delay(2000)
            await page.click('a.btn-primary.ok');
            // await clickElementWithText(page,'a',"预订",false,buyInfo.index)
            // await delay(2000)
            // await clickElementWithText(page,'label',pkgItem.buyName,false,0)
            // await delay(500)
            // await clickElementWithText(page,'a',"提交订单",false,0)
            // await delay(2000)
            // await clickElementWithText(page,'a',"确认",false,0)
        }else{
            console.log('没有符合条件的车次')
        }

    },
    async startPuppeteer(){
        if(hasStartPuppeteer){return}
        hasStartPuppeteer=true
        let launchConfig={}
        if(process.platform==='win32'){
            launchConfig={
                args: ['--start-maximized'],
                headless: false,
                // userDataDir:'/Users/caoke/chromedefault',
                userDataDir:'C:/Users/Y/AppData/Local/Google/Chrome/User Data/Default',
                // 'executablePath':'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
                'executablePath':"C:/Program Files/Google/Chrome/Application/chrome.exe"

            }
        }else{
            launchConfig={
                headless: false,
                userDataDir:'/Users/caoke/chromedefault',
                'executablePath':'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
            }
        }
        browser = await puppeteer.launch(launchConfig);
        page = await browser.newPage();
        await page.setViewport({ width:1600, height:768 });
    },
    async closePuppeteer(){
        hasStartPuppeteer=false
        await page.close();
        await browser.close();
    },
    async login(pkgItem){
        // 请替换为实际的用户名和密码
        const username = pkgItem.userName;
        const password = pkgItem.password;
        // 等待 3 秒
        await delay(3000);
        // 定义一个函数来判断是否为登录页
        async function isLoginPage(page) {
            const currentUrl = await page.url();
            const isLoginUrl = currentUrl.includes('loginAuth.html');
            return isLoginUrl
        }

        // 调用函数判断是否为登录页
        const isLogin = await isLoginPage(page);
        console.log('是否为登录页:', isLogin);
        if (isLogin) {
            // 等待 input.userAccount 元素出现，最多等待 10000 毫秒（10 秒）
            await page.waitForSelector('input.userAccount', { timeout: 10000 });
            // 输入用户名
            await page.type('input.userAccount', username);

            // 输入密码
            await page.type('input.hwid-input-pwd', password);

            await clickElementWithText(page,'span','登录',true)
            await delay(2000);
        }
        return isLogin
    },
}
view.init()
module.exports=view
